import { Injectable, Inject } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { JwtStrategys } from './strategies/jwt.strategy';
import { JWTUser } from 'src/common/interfaces/request';
import { IRefreshTokenRepository, REFRESH_TOKEN_REPOSITORY } from './interfaces/refresh-token.interface';
import { v4 as uuidv4 } from 'uuid';
import { InjectRepository } from '@nestjs/typeorm';
import { AdminUser } from 'src/endpoints/admin/admin-users/entities/admin-user.entity';
import { Repository } from 'typeorm';
import { User } from 'src/endpoints/app/users/entities/user.entity';

export interface TokenPair {
	access_token: string;
	refresh_token: string;
	expires_in: number;
}

@Injectable()
export class AuthService {
	constructor(
		private jwtService: JwtService,
		private configService: ConfigService,
		@Inject(REFRESH_TOKEN_REPOSITORY)
		private refreshTokenRepository: IRefreshTokenRepository,
		@InjectRepository(AdminUser)
		private adminUserRepository: Repository<AdminUser>,
		@InjectRepository(User)
		private readonly usersRepository: Repository<User>,
	) { }

	/**
	 * 生成双token对 (access token + refresh token)
	 */
	async generateTokenPair(payload: JWTUser, strategyName: keyof typeof JwtStrategys = 'app'): Promise<TokenPair> {
		// 转换为纯对象
		const tokenPayload = {
			userId: payload.userId,
			roles: payload.roles || [],
			customData: payload.customData || {}
		};

		const secret = this.configService.get(JwtStrategys[strategyName].secretOrKeyPath);
		const expiresIn = this.configService.get(JwtStrategys[strategyName].expiresInPath);

		// 生成access token (短期有效)
		const accessToken = this.jwtService.sign(tokenPayload, {
			secret: secret,
			expiresIn: expiresIn
		});

		// 生成refresh token ID
		const refreshTokenId = uuidv4();

		// refresh token 有效期 (默认7天)
		const refreshExpiresIn = this.configService.get(`jwt.${strategyName}.refreshExpiresIn`) || '7d';
		const refreshExpiresAt = this.getExpirationDate(refreshExpiresIn);

		// 保存refresh token到存储（忽略存储失败的错误）
		try {
			await this.refreshTokenRepository.save(
				payload.userId,
				refreshTokenId,
				refreshExpiresAt,
				strategyName
			);
		} catch (saveError) {
			console.warn('Failed to save refresh token to storage:', saveError.message);
			// 存储失败不影响token生成，继续返回token对
		}

		// 生成refresh token JWT
		const refreshToken = this.jwtService.sign(
			{ userId: payload.userId, tokenId: refreshTokenId, type: 'refresh' },
			{ secret: secret, expiresIn: refreshExpiresIn }
		);

		return {
			access_token: accessToken,
			refresh_token: refreshToken,
			expires_in: this.getExpirationSeconds(expiresIn)
		};
	}

	/**
	 * 兼容旧版本的单token生成方法
	 */
	generateToken(payload: JWTUser, strategyName: keyof typeof JwtStrategys = 'app') {
		// 转换为纯对象
		const tokenPayload = {
			userId: payload.userId,
			roles: payload.roles || [],
			customData: payload.customData || {}
		};

		const secret = this.configService.get(JwtStrategys[strategyName].secretOrKeyPath);
		const expiresIn = this.configService.get(JwtStrategys[strategyName].expiresInPath);
		return {
			access_token: this.jwtService.sign(tokenPayload, {
				secret: secret,
				expiresIn: expiresIn
			})
		};
	}

	/**
	 * 使用refresh token刷新access token
	 */
	async refreshToken(refreshToken: string, strategyName: keyof typeof JwtStrategys = 'app'): Promise<TokenPair | null> {
		try {
			const secret = this.configService.get(JwtStrategys[strategyName].secretOrKeyPath);
			const decoded = await this.jwtService.verifyAsync(refreshToken, { secret });

			if (decoded.type !== 'refresh') {
				return null;
			}

			// 验证refresh token是否在存储中有效
			// 如果存储服务异常，捕获错误并返回null，让客户端重新登录
			let isValid = false;
			try {
				isValid = await this.refreshTokenRepository.verify(
					decoded.userId,
					decoded.tokenId,
					strategyName
				);
			} catch (storageError) {
				console.warn('Refresh token storage verification failed:', storageError.message);
				// 存储服务异常，返回null让客户端重新登录
				return null;
			}

			if (!isValid) {
				return null;
			}

			// 撤销旧的refresh token（忽略撤销失败的错误）
			try {
				await this.refreshTokenRepository.revoke(
					decoded.userId,
					decoded.tokenId,
					strategyName
				);
			} catch (revokeError) {
				console.warn('Failed to revoke old refresh token:', revokeError.message);
				// 撤销失败不影响新token生成，继续执行
			}

			let user = null
			if (strategyName === 'admin') {
				user = await this.adminUserRepository.findOne({ where: { id: Number(decoded.userId) } });
				if (!user) {
					return null;
				}
			}
			if (strategyName === 'app') {
				user = await this.usersRepository.findOne({ where: { id: Number(decoded.userId) } });
				if (!user) {
					return null;
				}
			}

			// 生成新的token对
			const payload: JWTUser = {
				userId: user.id.toString(),
				roles: user.roles || [],
				customData: {
					username: user.username,
					nickname: user.nickname
				}
			};

			return await this.generateTokenPair(payload, strategyName);
		} catch (error) {
			console.warn('Refresh token failed:', error.message);
			return null;
		}
	}

	/**
	 * 撤销用户的refresh token
	 */
	async revokeRefreshToken(userId: string, tokenId: string, strategyName: keyof typeof JwtStrategys = 'app'): Promise<void> {
		try {
			await this.refreshTokenRepository.revoke(userId, tokenId, strategyName);
		} catch (error) {
			console.warn('Failed to revoke refresh token:', error.message);
			// 撤销失败不抛出异常，避免影响业务流程
		}
	}

	/**
	 * 撤销用户所有的refresh token
	 */
	async revokeAllRefreshTokens(userId: string, strategyName: keyof typeof JwtStrategys = 'app'): Promise<void> {
		try {
			await this.refreshTokenRepository.revokeAll(userId, strategyName);
		} catch (error) {
			console.warn('Failed to revoke all refresh tokens:', error.message);
			// 撤销失败不抛出异常，避免影响业务流程
		}
	}

	verifyAsync(token: string, strategyName: keyof typeof JwtStrategys = 'app') {
		const secret = this.configService.get(JwtStrategys[strategyName].secretOrKeyPath);
		return this.jwtService.verifyAsync(token, { secret });
	}

	/**
	 * 将时间字符串转换为Date对象
	 */
	private getExpirationDate(expiresIn: string): Date {
		const now = new Date();
		const match = expiresIn.match(/^(\d+)([smhd])$/);

		if (!match) {
			// 默认7天
			return new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
		}

		const value = parseInt(match[1]);
		const unit = match[2];

		switch (unit) {
			case 's':
				return new Date(now.getTime() + value * 1000);
			case 'm':
				return new Date(now.getTime() + value * 60 * 1000);
			case 'h':
				return new Date(now.getTime() + value * 60 * 60 * 1000);
			case 'd':
				return new Date(now.getTime() + value * 24 * 60 * 60 * 1000);
			default:
				return new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
		}
	}

	/**
	 * 将时间字符串转换为秒数
	 */
	private getExpirationSeconds(expiresIn: string): number {
		const match = expiresIn.match(/^(\d+)([smhd])$/);

		if (!match) {
			return 3600; // 默认1小时
		}

		const value = parseInt(match[1]);
		const unit = match[2];

		switch (unit) {
			case 's':
				return value;
			case 'm':
				return value * 60;
			case 'h':
				return value * 60 * 60;
			case 'd':
				return value * 24 * 60 * 60;
			default:
				return 3600;
		}
	}
}
